import geom.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.ListIterator;
import processing.core.*;
public class Layout {
	public static float P_INF = 32768;
	public static float N_INF = -32768;
	ArrayList prevPlaces;
	ArrayList prevAnchors;
	Anchor lastAnchor;
	Space lastSpace;
	Borg borg;
	Borg oldBorg;
	RectNode lastRect;
	PFont font;
	public Layout(PFont font) {
		borg = new Borg();
		prevPlaces = new ArrayList();
		prevAnchors = new ArrayList();
		lastAnchor = null;
		this.font = font;
	}
	
	public void undo(){
		if(oldBorg == null) return;
		borg = oldBorg;
	}
	
	public void add(RectNode rn, int side){
		oldBorg = borg;
		if(borg.numRects() <= 0){
			System.out.println("firstAdded");
			rn.x = 0;
			rn.y = 0;
			borg.assimilate(rn);
		}
		else{
			ArrayList edges = borg.getEdges(side);
			
			ArrayList spaces = getPlaces(rn, edges);
			System.out.println("gotSpaces" + spaces.size() + listToString(spaces));
			filterSpaces(spaces, rn);
			System.out.println("filteredSpaces" + spaces.size() + listToString(spaces));
			ArrayList anchors = getAllAnchors(edges);
			System.out.println("gotAnchorsSpaces" + anchors.size() + listToString(anchors));
			filterAnchors(anchors);
			initAnchors(anchors, spaces);
			System.out.println("init&filterAnchorsSpaces" + anchors.size() + listToString(anchors));
			
			Anchor anchor = Anchor.getBestAnchor(rn, borg.bounds, anchors);
			System.out.println("selectedPlace" + anchor.toString());
			
			anchor.placeRect(rn, anchor.getSpace(anchor.prefSpace));
			borg.assimilate(rn);
			Anchor.sortAnchors(anchors);
			System.out.println("addedRect");
			lastAnchor = anchor;
			prevPlaces = spaces;
			prevAnchors = anchors;
			lastRect = rn;
		}
	}
	
	public ArrayList getPlaces(RectNode nr, ArrayList edges){
		ArrayList places = new ArrayList();
		Iterator i = edges.iterator();
		while(i.hasNext()){
			Edge e = (Edge)i.next();
			ArrayList newp = getPlaces(nr, e);
			System.out.println(newp.size() + " places found on " + e.toString());
			places.addAll(newp);
		}
		return places;
		
	}
	
	public ArrayList getPlaces(RectNode nr, Edge e){
		ArrayList x = borg.getIntersects(e, Intersect.TOUCH);
		//System.out.println("gotIntersects " + x.size());
		//System.out.println(listToString(x));
		x = borg.filterIntersects(e, x);
		System.out.println("filteredIntersects " + x.size());
		System.out.println(listToString(x));
		setAnchors(e, x);
		System.out.println("setAnchors " + e.anchorSize());
		//System.out.println(listToString(e.anchors));
		ArrayList places = getRawPlaces(e, x);
		System.out.println("gotRawPlaces " + places.size());
		System.out.println(listToString(places));
		expandPlaces(e, places);
		System.out.println("expandedPlaces " + places.size());
		System.out.println(listToString(places));
		
		//System.out.println(listToString(places));
		return places;
	}
	
	public void draw(PApplet pa){
		pa.noFill();
		pa.stroke(255);
		borg.bounds.draw(pa);
		pa.stroke(0, 0, 240, 100);
		pa.fill(30, 40, 100, 100);
		Iterator i = borg.rects.iterator();
		while(i.hasNext())
			((RectNode)i.next()).draw(pa);
		pa.noFill();
		pa.stroke(250, 30, 10, 80);
		i = borg.getEdges().iterator();
		while(i.hasNext())
			((Edge)i.next()).draw(pa, font);
		
		pa.fill(10, 190, 20, 20);
		pa.stroke(10, 190, 20, 0);
		pa.noStroke();
		i = prevPlaces.iterator();
		while(i.hasNext())
			((Space)i.next()).draw(pa);
		if(lastAnchor != null){
			pa.noFill();
			pa.stroke(200, 20, 20);
			pa.ellipse(lastAnchor.x, lastAnchor.y, 8, 8);
		}
		if(lastSpace != null){
			pa.noFill();
			pa.stroke(70, 60, 230);
			lastSpace.draw(pa);
		}
		
		pa.noStroke();
		pa.fill(255, 30, 40);
		i = prevAnchors.iterator();
		while(i.hasNext()){
			((Anchor)i.next()).draw(pa, font);
		}
	}

	private ArrayList getRawPlaces(Edge e, ArrayList x){
		ArrayList places = new ArrayList();
		ListIterator i = x.listIterator();
		Intersect prev = null;
		while(i.hasNext()){
			Intersect cur = (Intersect)i.next();
			if(cur.type > Intersect.TOUCH){
				if(prev != null){
					float l = P_INF;
					if(e.hilo == Edge.LOW) l = N_INF; 
					Edge xe = new Edge( 	(prev.iPos.x + cur.iPos.x) / 2f, 
										(prev.iPos.y + cur.iPos.y) / 2f,
										l, cur.e2.dir, cur.e2.hilo);
					ArrayList xx = borg.getIntersects(xe, Intersect.BLOCKED);
					Space space = new Space();
					space.x = prev.iPos.x;
					space.y = prev.iPos.y;
					float dp = cur.e2.getPerpVal() - prev.e2.getPerpVal();
					if(e.dir == Edge.HORIZONTAL){
						space.w = dp;
						space.h = borg.getClosestIntersect(e, xx);
						if(e.hilo == Edge.LOW){
							space.y -= space.h;
						}
					}
					else{
						space.w = borg.getClosestIntersect(e, xx);
						space.h = dp;
						if(e.hilo == Edge.LOW){
							space.x -= space.w;
						}
					}
					space.edge = e;
					places.add(space);
				}
				prev = cur;
			}
			
		}
		return places;
	}
	
	public void setAnchors(Edge e, ArrayList x){
		Iterator i = x.iterator();
		e.clearAnchors();
		while(i.hasNext()){
			Intersect ix = (Intersect)i.next();
			if(e.onEdge(ix.iPos)){
				e.addAnchor(new Anchor(e, ix.iPos.x, ix.iPos.y, true));	
				e.addAnchor(new Anchor(e, ix.iPos.x, ix.iPos.y, false));	
			}
		}
	}
	
	public ArrayList getAllAnchors(ArrayList ea){
		ArrayList anchors = new ArrayList();
		Iterator e = ea.iterator();
		while(e.hasNext()){
			Edge edge = (Edge)e.next();
			Iterator a = edge.anchors.iterator();
			while(a.hasNext()){
				anchors.add(a.next());
			}
		}
		return anchors;
	}
	
	public void filterAnchors(ArrayList anchors){
		ListIterator a = anchors.listIterator();
		System.out.println("startFilterAnchors");
		for(int i = 0; i < anchors.size(); i++){
			Anchor a1 = (Anchor)anchors.get(i);
			ListIterator aa = anchors.listIterator();
			for(int ii = 0; ii < anchors.size(); ii++){
				Anchor a2 = (Anchor)anchors.get(ii);
				if(a1.isEqual(a2) && (i != ii)){
					a1.merge(a2);
					anchors.remove(ii);
					ii--;
					if(ii < i) 
						i--;
				}
			}
		}
		System.out.println("stopFilterAnchors");
	}
	
	
	public void filterAnchors_CONCURRENT_MOD(ArrayList anchors){
		ListIterator a = anchors.listIterator();
		System.out.println("startFilterAnchors");
		while(a.hasNext()){
			Anchor a1 = (Anchor)a.next();
			ListIterator aa = anchors.listIterator();
			while(aa.hasNext()){
				Anchor a2 = (Anchor)aa.next();
				if(a1.isEqual(a2) && (a.previousIndex() != aa.previousIndex())){
					a1.merge(a2);
					aa.remove();
				}
			}
		}
		System.out.println("stopFilterAnchors");
	}
	
	public void initAnchors(ArrayList anchors, ArrayList spaces){
		Iterator a = anchors.iterator();
		while(a.hasNext()){
			Anchor anchor = (Anchor)a.next();
			Iterator s = spaces.iterator();
			System.out.println("checking all spaces space to anch.");
			while(s.hasNext()){
				Space space = (Space)s.next();
				System.out.println("going to added space to anch.");
				if(space.onEdge(anchor)){
					anchor.addSpace(space);
					System.out.println("added space to anch.");
				}
			}
		}
	}

	private void expandPlaces(Edge e, ArrayList x){
		for(int index = 0; index < x.size(); index++){
				
			boolean sFound = false;
			boolean eFound = false;
			
			ListIterator i = x.listIterator(index);
			Space place = (Space)i.next();
			while(i.hasNext() && !eFound){
				Space cur = (Space)i.next();
				if(e.dir == Edge.HORIZONTAL){
					if(place.h <= cur.h){
						place.w += cur.w;
					}
					else eFound = true;
				}
				else{
					if(place.w <= cur.w){
						place.h += cur.h;
					}
					else eFound = true;
				}
			}
			i = x.listIterator(index);
			while(i.hasPrevious() && !sFound){
				Space cur = (Space)i.previous();
				if(e.dir == Edge.HORIZONTAL){
					if(place.h <= cur.h){
						place.w += cur.w;
						place.x = cur.x;
					}
					else sFound = true;
				}
				else{
					if(place.w <= cur.w){
						place.h += cur.h;
						place.y = cur.y;
					}
					else sFound = true;
				}
			}
		}
	}
	
	private void filterSpaces(ArrayList x, RectNode rn){
		ListIterator i = x.listIterator();
		while(i.hasNext()){
			Space s = (Space)i.next();
			boolean contained = false;
			Iterator ii = x.iterator();
			while(ii.hasNext()){
				Space sii = (Space)ii.next();
				if(sii.contains(s) && s.id != sii.id){
					contained = true;
				}
			}
			if(contained) i.remove();
		}
	}
	

	

	public String listToString(ArrayList l){
		StringBuffer sb = new StringBuffer();
		sb.append("LIST:::");
		sb.append(l.size());
		Iterator i = l.iterator();
		int p = 0;
		while(i.hasNext()){
			sb.append("\n" + p + "  ");
			p++;
			Object o = i.next();
			if(o instanceof Edge)
				sb.append(((Edge)o).toString());
			else if(o instanceof Space)
				sb.append(((Space)o).toString());
			else if(o instanceof Intersect)
				sb.append(((Intersect)o).toString());
			else if(o instanceof Anchor)
				sb.append(((Anchor)o).toString());
		}
		return sb.toString();
	}

	
}
